package com.xiaoxx.utils;

import com.alibaba.fastjson.JSONObject;
import com.xiaoxx.common.base.JwtUser;
import com.xiaoxx.common.error.ErrorException;
import com.xiaoxx.common.result.R;
import com.xiaoxx.common.result.RType;
import com.xiaoxx.enums.ResdisKeyEnum;
import io.jsonwebtoken.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * @Author HX
 * @Date 2023/1/6 15:47
 */
@Component
public class JwtUtil {
    private static StringRedisTemplate redisTemplate;

    @Autowired
    public void setStringRedisTemplate(@Autowired StringRedisTemplate stringRedisTemplate) {
        JwtUtil.redisTemplate = stringRedisTemplate;
    }

    //主题
    private static final String SUBJECT = "xiaoxx";
    //进行数字签名的私钥
    private static final String SECRET_KEY = "xiaoxx-!@#$";

    // 用户信息key
    private static final String AUTH_USER = "jwtUser";

    // TOKEN KEY值
    public static String TOKEN = "Authorization";

    // TOKEN 有效期 key值, 刷新 token时动态刷新
    public static String EXPIRED_TIME = "EXPIRED_TIME";

    public static String createToken(JwtUser jwtUser, HttpServletResponse response) {
        String jwtToken = Jwts.builder()
                //主题
                .setSubject(SUBJECT)
                // 添加jwt自定义参数 用户信息
                .claim(AUTH_USER, JSONObject.toJSONString(jwtUser))
                //token真实有效期
                .claim(EXPIRED_TIME, System.currentTimeMillis() + (1000L * 60 * (jwtUser.getExpiration())))
                //token 过期时间
                .setExpiration(new Date(System.currentTimeMillis() + (1000L * 60 * (jwtUser.getExpiration() / 2 == 0 ? 1 : jwtUser.getExpiration() / 2))))
                //签发时间
                .setIssuedAt(new Date())
                //加密方式,加密key
                .signWith(SignatureAlgorithm.HS256, SECRET_KEY).compact();
        // 放入Header
        response.setHeader(TOKEN, jwtToken);
        return jwtToken;
    }

    /**
     * 获取用户信息（自动区分登录类型--生成jwt时指定, jwtUser的type ）
     * 任意地方获取用户信息
     *
     * @return
     */
    public static JwtUser getJwtUser(HttpServletRequest request) {
        R<JwtUser> jwtUser = getJwtUserR(request, null);
        if (!jwtUser.getCode().equals(RType.SYS_SUCCESS.getValue())) {
            throw new ErrorException(jwtUser.getCode(), jwtUser.getMsg());
        }
        return jwtUser.getData();
    }


    /**
     * 获取 R<JwtUser>
     * 用于校验token
     *
     * @return
     */
    public static R<JwtUser> getJwtUserR(HttpServletRequest request, HttpServletResponse response) {
        String token = request.getHeader(TOKEN);
        try {
            //判断是否有token
            if (!StringUtils.hasLength(token)) {
                return R.error(RType.AUTHORITY_LOGIN_EXPIRED);
            }
            Claims claims = Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();
            return R.success(getClaimsJwtUser(claims));
        } catch (SignatureException ex) {
            //JWT签名与本地计算签名不匹配
            return R.error(RType.AUTHORITY_JWT_SIGN_ERROR);
        } catch (ExpiredJwtException ex) {
            //token 过期
            Claims claims = ex.getClaims();
            JwtUser jwtUser = getClaimsJwtUser(claims);
            //response 为空返回用户信息
            if (response == null) {
                return R.success(jwtUser);
            }
            //拿到token真实有效时间
            long relTime = Long.parseLong(claims.get(EXPIRED_TIME).toString());
            if (System.currentTimeMillis() > relTime) {
                return R.error(RType.AUTHORITY_LOGIN_EXPIRED);
            }
            // token在真实有效期内, 刷新token
            String newToken = createToken(jwtUser, response);
            redisTemplate.opsForValue().set(ResdisKeyEnum.LOGIN_TOKEN.getRedisPath().concat(jwtUser.getUserId()), newToken, 60, TimeUnit.MINUTES);
            return R.success(jwtUser);
        } catch (Exception e) {
            /**
             * JWT解析错误
             */
            return R.error(RType.AUTHORITY_JWT_PARSING_ERROR);
        }

    }

    /**
     * 获取用户信息的具体方法
     *
     * @param claims
     * @return
     */
    private static JwtUser getClaimsJwtUser(Claims claims) {
        // user 信息
        String deflaterJwtUser = claims.get(AUTH_USER).toString();
        JwtUser jwtUser = JSONObject.parseObject(deflaterJwtUser, JwtUser.class);
        return jwtUser;
    }

}
